package com.bes.bessdk.connect;

import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_NOTIFY;
import static android.bluetooth.BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE;
import static com.bes.bessdk.BesSdkConstants.ALIPAY_SERIVICE_UUID;
import static com.bes.bessdk.BesSdkConstants.BES_BLE_OTA_AUTO_TEST;
import static com.bes.bessdk.BesSdkConstants.BES_BLE_OTA_AUTO_TEST_VALUE;
import static com.bes.bessdk.BesSdkConstants.BES_CONNECT_ERROR;
import static com.bes.bessdk.BesSdkConstants.BES_CONNECT_SUCCESS;
import static com.bes.bessdk.BesSdkConstants.BES_GATT_DESCRIPTOR_OTA_UUID;
import static com.bes.bessdk.BesSdkConstants.BES_NOTIFY_ERROR;
import static com.bes.bessdk.BesSdkConstants.BES_NOTIFY_SUCCESS;
import static com.bes.bessdk.BesSdkConstants.BES_SAVE_LOG_OTA;
import static com.bes.bessdk.BesSdkConstants.BesConnectState;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONFIG_ERROR;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONNECT;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONNECT_NOTOTA;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_CONNECT_TOTA;
import static com.bes.bessdk.BesSdkConstants.BesConnectState.BES_NO_CONNECT;
import static com.bes.bessdk.BesSdkConstants.DEFAULT_MTU;
import static com.bes.sdk.utils.DeviceProtocol.PROTOCOL_BLE;
import static com.bes.sdk.utils.DeviceProtocol.PROTOCOL_GATT_BR_EDR;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.util.Log;

import com.bes.bessdk.BesSdkConstants;
import com.bes.bessdk.scan.BtHeleper;
import com.bes.bessdk.service.base.BesBaseConnectListener;
import com.bes.bessdk.service.base.BesServiceConfig;
import com.bes.bessdk.utils.ArrayUtil;
import com.bes.bessdk.utils.SPHelper;
import com.bes.sdk.connect.DeviceConnector;
import com.bes.sdk.device.HmDevice;
import com.bes.sdk.message.BaseMessage;
import com.bes.sdk.utils.DeviceProtocol;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BleConnector implements DeviceConnector {
    private static final String Tag = "BleConnector";
    protected final String TAG = getClass().getSimpleName();
    private static volatile BleConnector mBleConnector;
    private static BesBaseConnectListener mBesBaseConnectListener;
    private static Context mContext;
    private static List<BesServiceConfig> mServiceConfigs;
    private static List<BluetoothGatt> mBluetoothGattList;
    private static List<BluetoothGattCharacteristic> mCharacteristics;
    private static List<UUID> mDescriptors;
    private static List<HmDevice> mHmDevices;
    private static List<ConnectionListener> mConnectListeners;
    private static List<byte[]> mTotaAesKeys;
    private final Object mListenerLock = new Object();
    private HmDevice mHmDevice;
    private int connectMark = -1;
    private int mMtu = 0;
    private boolean isDiscoverServices = false;
    private boolean isConnecting = false;// add by wangsm

    public static BleConnector getsConnector(Context context, BesBaseConnectListener besBaseConnectListener, BesServiceConfig serviceConfig) {
        if (mBleConnector == null) {
            synchronized (BleConnector.class) {
                if (mBleConnector == null) {
                    mBleConnector = new BleConnector();
                    mServiceConfigs = new ArrayList<>();
                    mConnectListeners = new ArrayList<>();
                    mBluetoothGattList = new ArrayList<>();
                    mCharacteristics = new ArrayList<>();
                    mDescriptors = new ArrayList<>();
                    mHmDevices = new ArrayList<>();
                    mTotaAesKeys = new ArrayList<>();
                }
            }
        }
        if (context != null) {
            mContext = context;
        }
        if (besBaseConnectListener != null) {
            mBesBaseConnectListener = besBaseConnectListener;
        }
        if (serviceConfig != null && serviceConfig.getDevice() != null && mBleConnector.isNewDeviceWithConfig(serviceConfig)) {
            mHmDevices.add(serviceConfig.getDevice());
            mServiceConfigs.add(serviceConfig);
            mBluetoothGattList.add(null);
            mConnectListeners.add(null);
            mCharacteristics.add(null);
            mDescriptors.add(null);
            mTotaAesKeys.add(null);
        }
        if (serviceConfig != null) {
            ArrayUtil.addObjectIndex(mServiceConfigs, serviceConfig, mBleConnector.getCurIndex(mBleConnector.getCurDevice(serviceConfig.getDevice())));
        }

        return mBleConnector;
    }

    public ArrayList<HmDevice> getCurConnectDevices() {
        ArrayList<HmDevice> curConDevices = new ArrayList<>();
        for (int i = 0; i < mHmDevices.size(); i++) {
            BesServiceConfig config = mServiceConfigs.get(i);
            if (mCharacteristics.get(i) != null && config != null && (config.getTotaConnect() ? mTotaAesKeys.get(i) != null : true)) {
                curConDevices.add(mHmDevices.get(i));
            }
        }
        return curConDevices;
    }

    public BesConnectState getDeviceConnectState(BesServiceConfig serviceConfig) {
        HmDevice device = serviceConfig.getDevice();
        UUID serviceUUID = serviceConfig.getServiceUUID();
        UUID characteristicsUUID = serviceConfig.getCharacteristicsUUID();
        UUID descriptorUUID = serviceConfig.getDescriptorUUID();
        if (device == null || serviceUUID == null || characteristicsUUID == null || descriptorUUID == null) {
            return BES_CONFIG_ERROR;
        }
        BluetoothDevice mbtdevice = getCurDevice(device);
        BesServiceConfig oldConfig = getCurServiceConfig(mbtdevice);
        BluetoothGattCharacteristic btGattCharacteristic = getCurCharacteristic(mbtdevice);
        boolean isNewConfig = isNewDeviceWithConfig(serviceConfig);
        if (!isNewConfig && btGattCharacteristic != null && oldConfig != null && (oldConfig.getTotaConnect() ? getCurTotaAesKey(mbtdevice) != null : true)) {
            String serUUID = serviceConfig.getServiceUUID().toString();
            String oldSerUUID = oldConfig.getServiceUUID().toString();
            String characterUUID = serviceConfig.getCharacteristicsUUID().toString();
            String oldCharacterUUID = oldConfig.getCharacteristicsUUID().toString();
            String descUUID = serviceConfig.getDescriptorUUID().toString();
            String oldDescUUID = oldConfig.getDescriptorUUID().toString();
            if (serUUID.equals(oldSerUUID) && characterUUID.equals(oldCharacterUUID) && descUUID.equals(oldDescUUID)) {
                return BES_CONNECT;
            }
            return oldConfig.getTotaConnect() ? BES_CONNECT_TOTA : BES_CONNECT_NOTOTA;
        }
        return BES_NO_CONNECT;
    }

    private boolean isNewDeviceWithConfig(BesServiceConfig serviceConfig) {
        HmDevice hmDevice = serviceConfig.getDevice();
        for (int i = 0; i < mHmDevices.size(); i++) {
            HmDevice oldDevice = mHmDevices.get(i);
            if (serviceConfig.getDeviceProtocol() == PROTOCOL_BLE && hmDevice.getBleAddress() != null && hmDevice.getBleAddress().equals(oldDevice.getBleAddress())) {
                return false;
            } else if (serviceConfig.getDeviceProtocol() == PROTOCOL_GATT_BR_EDR && hmDevice.getDeviceMAC() != null && hmDevice.getDeviceMAC().equals(oldDevice.getDeviceMAC())) {
                return false;
            }
        }
        return true;
    }

    public void refreshConnectListener(ConnectionListener connectionListener, BluetoothDevice device) {
        ArrayUtil.addObjectIndex(mConnectListeners, connectionListener, getCurIndex(device));
    }

    public void saveTotaAesKey(byte[] data, BluetoothDevice device) {
        ArrayUtil.addObjectIndex(mTotaAesKeys, data, getCurIndex(device));
    }

    public byte[] getTotaAesKey(BluetoothDevice device) {
        return getCurTotaAesKey(device);
    }

    private BluetoothDevice getCurDevice(HmDevice hmDevice) {
        String address = hmDevice.getPreferredProtocol() == PROTOCOL_BLE ? hmDevice.getBleAddress() : hmDevice.getDeviceMAC();
        return BtHeleper.getBluetoothAdapter(mContext).getRemoteDevice(address);
    }

    private int getCurIndex(BluetoothDevice device) {
        for (int i = 0; i < mHmDevices.size(); i++) {
            HmDevice hmDevice = mHmDevices.get(i);
            if (hmDevice != null) {
                String address = hmDevice.getPreferredProtocol() == PROTOCOL_BLE ? hmDevice.getBleAddress() : hmDevice.getDeviceMAC();
                if (address.equals(device.getAddress())) {
                    return i;
                }
            }
        }
        return 10000;
    }

    private BesServiceConfig getCurServiceConfig(BluetoothDevice device) {
        int index = getCurIndex(device);
        if (mServiceConfigs.size() < index) {
            return null;
        }
        return mServiceConfigs.get(index);
    }

    private HmDevice getCurHmDevice(BluetoothDevice device) {
        if (mHmDevices.size() < getCurIndex(device)) {
            return null;
        }
        return mHmDevices.get(getCurIndex(device));
    }

    private BluetoothGatt getCurGatt(BluetoothDevice device) {
        if (mBluetoothGattList.size() < getCurIndex(device)) {
            Log.d(TAG, "getCurGatt is null ,retun");
            return null;
        }
        return mBluetoothGattList.get(getCurIndex(device));
    }

    private ConnectionListener getCurListener(BluetoothDevice device) {
        if (mConnectListeners.size() < getCurIndex(device)) {
            return null;
        }
        return mConnectListeners.get(getCurIndex(device));
    }

    private BluetoothGattCharacteristic getCurCharacteristic(BluetoothDevice device) {
        int index = getCurIndex(device);
        if (mCharacteristics.size() < index) {
            return null;
        }
        return mCharacteristics.get(index);
    }

    private byte[] getCurTotaAesKey(BluetoothDevice device) {
        if (mTotaAesKeys.size() < getCurIndex(device)) {
            return null;
        }
        return mTotaAesKeys.get(getCurIndex(device));
    }

    @Override
    public List<DeviceProtocol> getSupportedProtocols() {
        List<DeviceProtocol> sL = new ArrayList<>();
        sL.add(PROTOCOL_BLE);
        return sL;
    }

    @Override
    public boolean isProtocolSupported(DeviceProtocol deviceProtocol) {
        if (deviceProtocol == PROTOCOL_BLE) {
            return true;
        }
        return false;
    }

    @Override
    public void connect(HmDevice device) {
        if (device == null || mContext == null) {
            return;
        }
        startConnect(null, device);
    }

    @Override
    public void connect(HmDevice device, ConnectionListener connectionListener) {
        if (device == null || connectionListener == null || mContext == null) {
            return;
        }
        startConnect(connectionListener, device);
    }

    @SuppressLint("MissingPermission")
    private void startConnect(ConnectionListener connectionListener, HmDevice device) {
        if (device == null) {
            return;
        }
        isConnecting = true;// add by wangsm
        mHmDevice = device;
        connectMark++;
        Log.d(TAG, "connectMark: ------------->" + connectMark);

        BluetoothDevice mDevice = getCurDevice(mHmDevice);
        BluetoothGatt bluetoothGatt = getCurGatt(mDevice);
        if (device.getPreferredProtocol() == PROTOCOL_GATT_BR_EDR) {
            bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_BREDR);
            Log.d(TAG, "connectTimerTask: --------------PROTOCOL_GATT_BR_EDR");
        } else {
            if (connectMark == 1) {
                bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback);
                Log.d(TAG, "connectTimerTask: --------------1");
            } else if (connectMark == 2) {
                bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_LE, BluetoothDevice.PHY_LE_2M_MASK, null);
                bluetoothGatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED);
                Log.d(TAG, "connectTimerTask: --------------2");
            } else if (connectMark == 3) {
                bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
                Log.d(TAG, "connectTimerTask: --------------3");
            } else if (connectMark == 4) {
                bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback);
                Log.d(TAG, "connectTimerTask: --------------4");
            } else {
                boolean useNormalConnect = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_USE_NORMAL_CONNECT, BesSdkConstants.BES_USE_NORMAL_CONNECT_VALUE);
                boolean usePHY2M = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_USE_PHY_2M, BesSdkConstants.BES_USE_PHY_2MVALUE);
                if (useNormalConnect) {
                    LOG(TAG, "startConnect: ------useNormalConnect-NO_MASK");
                    Log.d(TAG, "connectTimerTask: --------------5");
                    bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback);
                } else if (BtHeleper.getBluetoothAdapter(mContext).isLe2MPhySupported() && usePHY2M) {
                    LOG(TAG, "startConnect: ------PHY_LE_2M_MASK");
                    Log.d(TAG, "connectTimerTask: --------------6");
                    bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_LE, BluetoothDevice.PHY_LE_2M_MASK, null);
                    bluetoothGatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED);
                } else {
                    LOG(TAG, "startConnect: -------PHY_LE_1M_MASK");
                    Log.d(TAG, "connectTimerTask: --------------7");
                    if (bluetoothGatt != null) {
                        Log.d(TAG, "startConnect(: --------------begin add  bluetoothGatt");
                        Log.d(TAG, "startConnect(: --------------the index is: " + getCurIndex(mDevice) + "the address is: " + mDevice.getAddress());
                        bluetoothGatt = mDevice.connectGatt(mContext, true, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
                    } else {
                        Log.d(TAG, "startConnect(: --------------the bluetoothGatt is null");
                        bluetoothGatt = mDevice.connectGatt(mContext, false, mBluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
                    }
                }
            }
        }
        Log.d(TAG, "startConnect(: --------------insert mBluetoothGattList into gatt from device");
        ArrayUtil.addObjectIndex(mBluetoothGattList, bluetoothGatt, getCurIndex(mDevice));
        ArrayUtil.addObjectIndex(mConnectListeners, connectionListener, getCurIndex(mDevice));
    }

    private boolean setWriteCharacteristic(UUID service, UUID characteristic, BluetoothDevice device) {
        LOG(TAG, "setWriteCharacteristic service----- " + service.toString() + "; characteristic " + characteristic.toString());
        BluetoothGatt gatt = getCurGatt(device);
        if (gatt == null) {
            LOG(TAG, "setWriteCharacteristic: ---gatt is null ,return--false");
            return false;
        }
        BluetoothGattService gattService = gatt.getService(service);
        if (gattService == null) {
            LOG(TAG, "setWriteCharacteristic: ---gattService is null ,return--false");
            return false;
        }
        BluetoothGattCharacteristic mCharacteristicTx = gattService.getCharacteristic(characteristic);
        if (mCharacteristicTx == null) {
            LOG(TAG, "setWriteCharacteristic: ---mCharacteristicTx is null ,return--false");
            return false;
        }
        LOG(TAG, "setWriteCharacteristic: ---mCharacteristicTx is has data ,return--true");
        ArrayUtil.addObjectIndex(mCharacteristics, mCharacteristicTx, getCurIndex(device));
        return true;
    }

    private boolean requestMtu(int mtu, BluetoothDevice device) {
        LOG(TAG, "requestMtu");
        BluetoothGatt gatt = getCurGatt(device);
        if (gatt != null) {
            return gatt.requestMtu(mtu);
        }
        return false;
    }

    private boolean enableCharacteristicNotification(UUID service, UUID rxCharacteristic, UUID descriptor, BluetoothDevice device) {
        Log.i(TAG, "enableCharacteristicNotification: " + rxCharacteristic);
        BluetoothGatt gatt = getCurGatt(device);
        if (gatt != null) {
            BluetoothGattService gattService = gatt.getService(service);
            if (gattService == null) {
                Log.i(TAG, "enableCharacteristicNotification: gattService is null ");
                return false;
            }
            BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(rxCharacteristic);
            if (gattCharacteristic == null) {
                Log.i(TAG, "enableCharacteristicNotification: gattCharacteristic is null ");
                return false;
            }
            Log.i(TAG, "enableCharacteristicNotification: getProperties is  " + gattCharacteristic.getProperties());
            if ((gattCharacteristic.getProperties() & PROPERTY_NOTIFY) == 0) {
                Log.i(TAG, "enableCharacteristicNotification: PROPERTY_NOTIFY is 0,return true ");
                onStatusChanged(gatt.getDevice(), true);
                return true;
            }
            BluetoothGattDescriptor gattDescriptor = gattCharacteristic.getDescriptor(descriptor);
            if (gattDescriptor == null) {
                return false;
            }
            if (!gatt.setCharacteristicNotification(gattCharacteristic, true)) {
                return false;
            }
            if (!gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
                LOG(TAG, "gattDescriptor.ENABLE fail: --------");
                try {
                    Thread.sleep(3000);
                    if (!gattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
                        return false;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            Log.i(TAG, "enableCharacteristicNotification: setCharacteristicNotification is true--------");
            ArrayUtil.addObjectIndex(mDescriptors, descriptor, getCurIndex(device));
            return gatt.writeDescriptor(gattDescriptor);
        }
        return false;
    }

    public boolean write(byte[] data, BluetoothDevice device) {
        LOG(TAG, "write: ------" + ArrayUtil.toHex(data));
        LOG(TAG, "write: ------" + device.getAddress());
        BluetoothGatt mBluetoothGatt = getCurGatt(device);
        BluetoothGattCharacteristic mCharacteristic = getCurCharacteristic(device);
        if (mBluetoothGatt != null && mCharacteristic != null) {
            mCharacteristic.setValue(data);
            mCharacteristic.setWriteType(WRITE_TYPE_NO_RESPONSE);
            boolean ret = mBluetoothGatt.writeCharacteristic(mCharacteristic);
            LOG(TAG, "write: ------" + device.getAddress() + "------ ret===" + ret);
            int count = 0;
            while (!ret && count < 6) {
                count++;
                LOG(TAG, "write: ------" + device.getAddress() + "------ ret===" + ret);
                ret = mBluetoothGatt.writeCharacteristic(mCharacteristic);
                SPHelper.putPreference(mContext, BesSdkConstants.BES_BLE_RETRY_TRANS_KEY, true);
            }
            return ret;
        } else {
            LOG(TAG, "write: ------ false");
            if (null == mBluetoothGatt) {
                LOG(TAG, "write: ------ mBluetoothGatt is null ");
            }
            if (null == mCharacteristic) {
                LOG(TAG, "write: ------ mCharacteristic is null ");
            }
        }

        return false;
    }

    public boolean writeWithoutResponse(byte[] data, BluetoothDevice device) {
        LOG(TAG, "write: ------" + ArrayUtil.toHex(data));
        BluetoothGatt mBluetoothGatt = getCurGatt(device);
        BluetoothGattCharacteristic mCharacteristic = getCurCharacteristic(device);
        mCharacteristic.setValue(data);
        mCharacteristic.setWriteType(WRITE_TYPE_NO_RESPONSE);
        boolean ret = mBluetoothGatt.writeCharacteristic(mCharacteristic);
        LOG(TAG, "writeWithoutResponse: ------" + ret + "-------" + data.length);
        return ret;
    }

    private void notifyReceive(byte[] data, BluetoothDevice device) {
        LOG(TAG, "notifyReceive: ------" + device.getAddress());
        BaseMessage baseMessage = new BaseMessage();
        baseMessage.setPush(true);
        baseMessage.setMsgContent(data);
        ConnectionListener connectionListener = getCurListener(device);
        if (connectionListener != null) {
            connectionListener.onDataReceived(baseMessage);
        }
    }

    public void close(BluetoothDevice device) {
        LOG(TAG, "close: ----" + device.getAddress());
        BluetoothGatt gatt = getCurGatt(device);
        if (gatt != null) {
            gatt.disconnect();
            gatt.close();
        }
        boolean bleOtaAutoTest = (boolean) SPHelper.getPreference(mContext, BES_BLE_OTA_AUTO_TEST, BES_BLE_OTA_AUTO_TEST_VALUE);
        // boolean bleOtaAutoTest = true;
        if (bleOtaAutoTest) {
            ConnectionListener connectionListener = getCurListener(device);
            if (connectionListener != null) {
                connectionListener.onStatusChanged(getCurHmDevice(device), BES_CONNECT_ERROR, PROTOCOL_BLE);
            }
            mServiceConfigs = new ArrayList<>();
            mConnectListeners = new ArrayList<>();
            mBluetoothGattList = new ArrayList<>();
            mCharacteristics = new ArrayList<>();
            mDescriptors = new ArrayList<>();
            mHmDevices = new ArrayList<>();
            mTotaAesKeys = new ArrayList<>();
            return;
        }
        // add by wangsm for index
        int currIndex = getCurIndex(device);
        ArrayUtil.resetObjectAtIndex(mServiceConfigs, currIndex);// getCurIndex(device)
        ArrayUtil.resetObjectAtIndex(mBluetoothGattList, currIndex);// getCurIndex(device)
        ArrayUtil.resetObjectAtIndex(mCharacteristics, currIndex);// getCurIndex(device)
        ArrayUtil.resetObjectAtIndex(mDescriptors, currIndex);// getCurIndex(device)

        ConnectionListener connectionListener = getCurListener(device);
        if (connectionListener != null) {
            connectionListener.onStatusChanged(getCurHmDevice(device), BES_CONNECT_ERROR, PROTOCOL_BLE);
        }
        // add by wangsm for remove disconnect device
        // ArrayUtil.resetObjectAtIndex(mHmDevices, currIndex);//getCurIndex(device)
    }

    @Override
    public void disconnect(HmDevice device) {
        LOG(TAG, "disconnect: -------");
        mMtu = 0;
        close(getCurDevice(device));
        onStatusChanged(getCurDevice(device), false);
    }

    @Override
    public void registerConnectionListener(ConnectionListener connectionListener) {
        synchronized (mListenerLock) {
            if (!mConnectListeners.contains(connectionListener)) {
                mConnectListeners.add(connectionListener);
            }
        }
    }

    @Override
    public void unregisterConnectionListener(ConnectionListener connectionListener) {
        synchronized (mListenerLock) {
            if (mConnectListeners.contains(connectionListener)) {
                mConnectListeners.remove(connectionListener);
            }
        }
    }

    private void onStatusChanged(BluetoothDevice device, boolean isSuccess) {
        int connStatus = isSuccess ? BES_CONNECT_SUCCESS : BES_CONNECT_ERROR;
        LOG(TAG, "onStatusChanged---" + device + "---" + isSuccess + ",connStatus:" + connStatus);
        isConnecting = false;
        ConnectionListener connectionListener = getCurListener(device);
        if (connectionListener != null) {
            connectionListener.onStatusChanged(getCurHmDevice(device), connStatus, PROTOCOL_BLE);
        }
        if (!isSuccess) {
            Log.d(TAG, "onStatusChanged close the device");
            close(device);
        }
    }

    public void LOG(String TAG, String msg) {
        if (mContext == null) {
            return;
        }
        Log.i(TAG, msg);
        String saveLogName = (String) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_NAME, "");
        boolean saveLog = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_KEY, BesSdkConstants.BES_SAVE_LOG_VALUE);
        if (saveLog) {
            if (saveLogName.equals(BES_SAVE_LOG_OTA)) {
                //LogUtils.writeForOTA(TAG, msg, saveLogName);
            } else if (!saveLogName.isEmpty()) {
                //LogUtils.writeForLOG(TAG, msg, saveLogName);
            }
        }
    }

    private final BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            LOG(TAG, "onConnectionStateChange: --------------status: " + status + " newState:" + newState);
            if (status == BluetoothGatt.GATT_FAILURE) {
                onStatusChanged(gatt.getDevice(), false);
                return;
            }
            if (status != BluetoothGatt.GATT_SUCCESS) {
                LOG(TAG, "onConnectionStateChange: --------------isConnecting:" + isConnecting);
                ConnectionListener connectionListener = getCurListener(gatt.getDevice());
                if (isConnecting && connectionListener != null) {
                    startConnect(connectionListener, mHmDevice);
                } else {
                    onStatusChanged(gatt.getDevice(), false);
                }
                return;
            }
            mHmDevice = null;
            connectMark = -1;
            ArrayUtil.addObjectIndex(mBluetoothGattList, gatt, getCurIndex(gatt.getDevice()));
            LOG(TAG, "onConnectionStateChange: --------------" + gatt.getDevice().getAddress());
            if (BtHeleper.getBluetoothAdapter(mContext).isLe2MPhySupported()) {
                gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED);
            }
            isDiscoverServices = false;
            if (newState == BluetoothGatt.STATE_CONNECTED && gatt.discoverServices()) {
                LOG(TAG, "gatt.discoverServices() is true: -----" + newState);
                Thread isDiscoverServicesThread = new Thread(() -> {
                    try {
                        Thread.sleep(3000);
                        if (!isDiscoverServices) {
                            onStatusChanged(gatt.getDevice(), false);
                            close(gatt.getDevice());
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                });
                isDiscoverServicesThread.start();
            } else {
                onStatusChanged(gatt.getDevice(), false);
                close(gatt.getDevice());
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            isDiscoverServices = true;
            LOG(TAG, "onServicesDiscovered status: -----" + status);
            if (gatt == null) {
                Log.d(TAG, "onServicesDiscovered,gatt is null");
                return;
            }
            if (BtHeleper.getBluetoothAdapter(mContext).isLe2MPhySupported()) {
                LOG(TAG, "onServicesDiscovered isLe2MPhySupported: -----");
                gatt.setPreferredPhy(BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_LE_2M_MASK, BluetoothDevice.PHY_OPTION_NO_PREFERRED);
            }
            BesServiceConfig serviceConfig = getCurServiceConfig(gatt.getDevice());
            if (serviceConfig == null) {
                Log.d(TAG, "onServicesDiscovered,serviceConfig is null");
                return;
            }
            try {
                UUID serviceUUID = getCurServiceConfig(gatt.getDevice()).getServiceUUID();
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    LOG(TAG, "onServicesDiscovered -----" + " status:" + status);
                    requestMtu(DEFAULT_MTU, gatt.getDevice());
                    Thread.sleep(500);
                    if (serviceUUID == ALIPAY_SERIVICE_UUID) {
                        enableCharacteristicNotification(BesSdkConstants.ALIPAY_SERIVICE_UUID, BesSdkConstants.ALIPAY_CHARACTERISTICTX_UUID, BES_GATT_DESCRIPTOR_OTA_UUID, gatt.getDevice());
                    } else {
                        enableCharacteristicNotification(serviceUUID, getCurServiceConfig(gatt.getDevice()).getCharacteristicsUUID(), getCurServiceConfig(gatt.getDevice()).getDescriptorUUID(), gatt.getDevice());
                    }
                } else {
                    LOG(TAG, "onServicesDiscovered 444: -----" + status);
                    onStatusChanged(gatt.getDevice(), false);
                    close(gatt.getDevice());
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            int state = (status == BluetoothGatt.GATT_SUCCESS) ? BES_NOTIFY_SUCCESS : BES_NOTIFY_ERROR;
            LOG(TAG, "onCharacteristicWrite: state-----" + state);
            if (getCurListener(gatt.getDevice()) != null) {
                getCurListener(gatt.getDevice()).notifyWrite(state);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            LOG(TAG, "onCharacteristicChanged: -----" + ArrayUtil.toHex(characteristic.getValue()));
            notifyReceive(characteristic.getValue(), gatt.getDevice());
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            LOG(TAG, "onDescriptorWrite: -----" + gatt.getDevice().getAddress());
            if (descriptor.getUuid().equals(getCurServiceConfig(gatt.getDevice()).getDescriptorUUID())) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    LOG(TAG, "onDescriptorWrite true: -----" + status);
                    BesServiceConfig serviceConfig = getCurServiceConfig(gatt.getDevice());
                    if (serviceConfig == null) {
                        Log.d(TAG, "onDescriptorWrite,serviceConfig is null");
                        return;
                    }
                    UUID characteristicsUUID = serviceConfig.getCharacteristicsTX() == null ? serviceConfig.getCharacteristicsUUID() : serviceConfig.getCharacteristicsTX();
                    setWriteCharacteristic(serviceConfig.getServiceUUID(), characteristicsUUID, gatt.getDevice());
                    onStatusChanged(gatt.getDevice(), true);
                    return;
                }
            }
            LOG(TAG, "onDescriptorWrite Fail: ---close()---" + status);
            onStatusChanged(gatt.getDevice(), false);
            close(gatt.getDevice());
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            LOG(TAG, "onMtuChanged: -----" + mtu + " status:" + status);
            UUID serviceUUID = getCurServiceConfig(gatt.getDevice()).getServiceUUID();
            if (status == BluetoothGatt.GATT_SUCCESS) {
                mMtu = mtu;
            }
            LOG(TAG, "onMtuChanged: ---serviceUUID--" + serviceUUID + " mMtu::" + mMtu);
        }
    };
}
